package com.hongdong.flowable.controller;

import com.hongdong.flowable.common.RequestResult;
import com.hongdong.flowable.entity.XmlDefinitions;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.annotations.FromAnnotationsRuleModule;
import org.apache.commons.digester3.binder.DigesterLoader;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.util.CollectionUtil;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.xml.sax.SAXException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RestController
@RequestMapping("/process")
@Slf4j
public class ProcessController extends WebBaseController{

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private Environment env;

    @PostMapping("/v1.0/model")
    public RequestResult<Object> model(){
        List<Model> rows = processEngine.getRepositoryService().createModelQuery().latestVersion().orderByCreateTime().asc().listPage(0,20);

        List<ProcessDefinition> rows1 =  repositoryService.createProcessDefinitionQuery().latestVersion().orderByProcessDefinitionName().asc().listPage(0,20);
        List<Model> rows2 =  repositoryService.createModelQuery().list();

        return success(rows);
    }


    /**
     * 数据库DB初始化
     *
     * @param params 传入参数
     * @return
     */
    @PostMapping("/v1.0/db/init")
    public RequestResult<Object> initDatabase(@RequestBody Map<String,Object> params){
      ProcessEngineConfiguration configuration = new StandaloneProcessEngineConfiguration()
                .setJdbcUrl("jdbc:mysql://127.0.0.1:3306/flowable?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&nullCatalogMeansCurrent=true")
                .setJdbcUsername("root")
                .setJdbcPassword("root")
                .setJdbcDriver("com.mysql.cj.jdbc.Driver")
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
      ProcessEngine processEngine = configuration.buildProcessEngine();
      log.info("数据库DB初始化成功:{}", processEngine.getDynamicBpmnService());
      return success("数据库DB初始化成功");
    }

    /**
     * 部署流程
     * select * from act_re_deployment
     *
     * @param multipartFile 文件流
     * @param response
     * @return
     */
    @PostMapping(value = "/v1.0/deployment",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public RequestResult<Object> deployment(@RequestPart("file") MultipartFile multipartFile,HttpServletRequest request, HttpServletResponse response) throws IOException, SAXException {

        // bpmn的xml解析process
        Digester digester = DigesterLoader.newLoader(new FromAnnotationsRuleModule() {
            @Override
            protected void configureRules() {
                bindRulesFrom(XmlDefinitions.class);
            }
        }).newDigester();

        XmlDefinitions xmlProcess = digester.parse(multipartFile.getInputStream());

        String key  = xmlProcess.getProcess().getId();
        String name = xmlProcess.getProcess().getName();

        // 查询部署流程切激活状态流程定义 将暂停
        List<ProcessDefinition> activeList =  repositoryService.createProcessDefinitionQuery().active().processDefinitionKey(key).list();
        if(CollectionUtil.isNotEmpty(activeList)){
            activeList.forEach(item ->{
                repositoryService.suspendProcessDefinitionById(item.getId());
            });
        }

        //创建部署信息和实例信息
        InputStream inputStream = multipartFile.getInputStream();
        String filename = multipartFile.getOriginalFilename();
        Deployment deployment =  repositoryService.createDeployment()
                .name(name)
                .key(key)
                .addInputStream(filename,inputStream)
                .deploy();
        log.info("部署成功:{}", deployment.getId());

        return success(deployment.getId());
    }

    /**
     * 创建流程
     *
     *
     * @param params 传入参数
     * @return
     */
    @PostMapping("/v1.0/create/{processKey}")
    public RequestResult<Object> createProcess(@PathVariable("processKey") String processKey, @RequestBody Map<String,Object> params){

        // 获得流程定义
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .active()
                .processDefinitionKey(processKey)
                .singleResult();

        //设置流程的创建人
        params.put("startUserId", params.get("owner"));
        params.put("START_USER_Id", params.get("owner"));
        params.put("START_USER_Id_", params.get("owner"));

        identityService.setAuthenticatedUserId((String)params.get("owner"));

        ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .processDefinitionId(definition.getId())
                .name(definition.getName().trim())
                .variables(params).start();

        log.info("ProcessInstanceId: {}",processInstance.getId());
        Map<String,Object> result = new HashMap<>();
        if(processInstance != null){
            result.put("id",processInstance.getId());
            result.put("processDefinitionId",processInstance.getProcessDefinitionId());
            result.put("processInstanceId",processInstance.getProcessInstanceId());
            result.put("name",processInstance.getName());
            result.put("businessKey",processInstance.getBusinessKey());
            result.put("startUserId",processInstance.getStartUserId());

            Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
            if(task != null){
                result.put("taskId",task.getId());
                taskService.complete(task.getId());
                result.put("taskName",task.getName());
            }
        }
        return success(result);
    }

    /**
     * 我的待办
     *
     * @param params
     * @return
     */
    @PostMapping("/v1.0/task/todo")
    public RequestResult<Object> queryCreateTask(@RequestBody Map<String,Object> params,@RequestParam("firstResult") int firstResult,@RequestParam("maxResults") int maxResults){
        String userId = (String) params.get("userId");
        List<Task> taskQuery = taskService.createTaskQuery().taskAssignee(userId).listPage(firstResult,maxResults);

        List<Map<String,Object>> list = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(taskQuery)){
            Map<String,Object> item = null;
            for (Task task : taskQuery){
                item = new HashMap<>();
                item.put("taskName",task.getName());
                item.put("taskId",task.getId());
                item.put("processInstanceId",task.getProcessInstanceId());
                item.put("owner",task.getOwner());
                item.put("assignee",task.getAssignee());
                item.put("createTime",task.getCreateTime());
                list.add(item);
            }
        }
        return success(list);
    }

    /**
     * 我的申请
     *  SELECT DISTINCT
     * 	RES.*,
     * 	DEF.KEY_ AS PROC_DEF_KEY_,
     * 	DEF.NAME_ AS PROC_DEF_NAME_,
     * 	DEF.VERSION_ AS PROC_DEF_VERSION_,
     * 	DEF.DEPLOYMENT_ID_ AS DEPLOYMENT_ID_
     * FROM
     * 	ACT_HI_PROCINST RES
     * 	LEFT OUTER JOIN ACT_RE_PROCDEF DEF ON RES.PROC_DEF_ID_ = DEF.ID_
     * WHERE
     * 	START_USER_ID_ is not null
     * ORDER BY
     * 	RES.START_TIME_ DESC
     * @param params
     * @return
     */
    @PostMapping("/v1.0/task/apply")
    public RequestResult<Object> queryTaskApply(@RequestBody Map<String,Object> params,@RequestParam("firstResult") int firstResult,@RequestParam("maxResults") int maxResults){
        String userId = (String) params.get("userId");
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery()
                .startedBy(userId).orderByProcessInstanceStartTime().desc().listPage(firstResult,maxResults);
        return success(list);
    }

    /**
     * 查询我的已办
     *
     * @param params
     * @return
     */
    @PostMapping("/v1.0/task/complete")
    public RequestResult<Object> queryTaskCompleted(@RequestBody Map<String,Object> params,@RequestParam("firstResult") int firstResult,@RequestParam("maxResults") int maxResults){
        String userId = (String) params.get("userId");
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(userId).finished().orderByHistoricTaskInstanceEndTime().desc()
                .listPage(firstResult,maxResults);
        return success(list);
    }

    /**
     * 审批通过
     *
     * @param taskId 任务Id
     * @param params 参数
     * @return
     */
    @PostMapping("/v1.0/approval/{taskId}")
    public RequestResult<Object> approvalTask(@PathVariable("taskId") String taskId, @RequestBody Map<String,Object> params){
       Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            return fail("流程不存在");
        }
        taskService.complete(taskId,params);
        return success("审批OK");
    }

    /**
     * 审批不通过
     *
     * @param taskId 任务Id
     * @param params 参数
     * @return
     */
    @PostMapping("/v1.0/reject/{taskId}")
    public RequestResult<Object>  rejectTask(@PathVariable("taskId") String taskId, @RequestBody Map<String,Object> params){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            return fail("流程不存在");
        }
        taskService.complete(taskId,params);
        return success("审批不通过");
    }

    /**
     * 流程图预览查看
     *
     * @param processId
     * @param httpServletResponse
     * @throws IOException
     */
    @GetMapping("/v1.0/diagram/{processId}")
    public void processDiagram(@PathVariable("processId") String processId,HttpServletResponse httpServletResponse) throws IOException {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();

        if (processInstance == null) {
            httpServletResponse.getWriter().println("not find ProcessInstance ...");
            return ;
        }

        Task task =  taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();

        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService.createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();

        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
            //if(exe.getReferenceType())
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows,
                engconf.getActivityFontName(), engconf.getLabelFontName(),
                engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;

        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        }catch(Exception exception){
            exception.printStackTrace();
        }finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

    /**
     * 流程图预览查看
     *
     * @param processId
     * @param httpServletResponse
     * @throws IOException
     */
    @GetMapping("/v2.0/diagram/{processId}")
    public void processDiagram2(@PathVariable("processId") String processId,HttpServletResponse httpServletResponse) throws IOException {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        String processDefinitionId;
        if (processInstance == null) {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
        } else {
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId=pi.getProcessDefinitionId();
        }

        List<String> highLightedActivitis = new ArrayList<String>();
        List<String> highLightedFlows = new ArrayList<>();

        List<HistoricActivityInstance> historicActivityList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list();
        historicActivityList.forEach((HistoricActivityInstance item) ->{
            if("sequenceFlow".equalsIgnoreCase(item.getActivityType())){
                highLightedFlows.add(item.getActivityId());
            }else{
                highLightedActivitis.add(item.getActivityId());
            }
        });

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "bmp", highLightedActivitis, highLightedFlows,
                engconf.getActivityFontName(), engconf.getLabelFontName(),
                engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;

        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        }catch(Exception exception){
            exception.printStackTrace();
        }finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}
